创建时间: | 2018/10/29 9:31 |
来源: | https://blog.wuwii.com/juc-utils.html |
java.util.concurrent
下提供了一些辅助类来帮助我们在并发编程的设计。
学习了 AQS 后再了解这些工具类,就非常简单了。
jdk 1.8
在 concurrent
包下面提供了 CountDownLatch
类,它提供了计数器的功能,能够实现让一个线程等待其他线程执行完毕才能进入运行状态。
首先看下最关键的地方它的自定义同步器的实现,非常简单:
|
|
在这里查看构造器的源码得知,CountDownLatch
内部使用的是 内部类Sync
继承了 AQS
,将我们传入进来的 count
数值当作 AQS state。感觉这个是不是和可重入锁实现是一样的,只不过开始指定了线程获取的锁的次数。
在上面我也发现了几个特点,第一次看这个代码其实还是不好理解,因为它相对前面的 AQS 和 TwinsLock 就是一个反着设计的代码:
countDown
都去减少一个,没有方法去增加数量,所以它是不可逆的,它的计数器是不可以重复使用的。看下 await 的实现,发现它最终实现的是 doAcquireSharedInterruptibly
:
|
|
CountDownLatch允许一个或多个线程等待其他线程完成操作。
比如经典问题:
有Thread1、Thread2、Thread3、Thread4四条线程分别统计C、D、E、F四个盘的大小,所有线程都统计完毕交给Thread5线程去做汇总,应当如何实现?
这个问题关键就是要知道四条线程何时执行完。
下面是我的解决思路:
|
|
CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。
|
|
|
|
|
|
CyclicBarrier可以用于多线程计算数据,最后合并计算结果的场景,然后四条线程又可以分别去干自己的事情了。
现在我将上面的统计磁盘的任务 CountDownLatch
中改下,统计完统计最终后,每个线程要发出退出信号。
下面是我的实现代码:
|
|
执行结果:
|
|
Semaphore
(信号量)是用来控制同时访问特定资源的线程数量(许可证数),它通过协调各个线程,以保证合理的使用公共资源。
|
|
具有公平锁的特性,permits
指定许可数量,就是资源数量 state
。
|
|
availablePermits
:获取此信号量中当前可用的许可证数(还能有多少个线程执行);drainPermits
:立刻使用完所有可用的许可证;reducePermits
:减少相应数量的许可证,是一个 protected
方法;isFair
:是否是公平状态;hasQueuedThreads
:等待队列中是否有线程,等待获取许可证;getQueueLength
:等待队列中等待获取许可证的线程数量;getQueuedThreads
:protected
方法,获取等待队列中的线程。Semaphore
可以用于做流量控制,特别是公用资源有限的应用场景,比如我们有五台机器,有十名工人,每个工人需要一台机器才能工作,一名工人工作完了就可以休息了,机器让其他没工作过的工人使用。
下面是我的实现代码:
|
|
执行一下结果:
|
|
虽然上面有 10 个工人(线程)一起并发,但是,它同时只有五个工人能够是执行的。
Exchanger(交换者)是一个用于线程间协作的工具类。Exchanger 用于两个工作线程间的数据交换。
具体上来说,Exchanger类允许在两个线程之间定义同步点。当两个线程都到达同步点时,他们交换数据结构,因此第一个线程的数据进入到第二个线程中,第二个线程的数据进入到第一个线程中,这要就完成了一个“交易”的环节。
源码很难看懂,主要还是
【死磕Java并发】—–J.U.C之并发工具类:Exchanger
Exchanger 可以用于遗传算法。遗传算法里需要选出两个人作为交配对象,这时候会交换两人的数据。
下面做一个卖书买书的例子:
|
|
执行结果:
|
|
Exchanger
主要完成的是两个工作线程之间的数据交换,如果有一个线程没有执行 exchange()
方法,则会一直等待。还可以设置最大等待时间exchange(V v, TimeUnit unit)
CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法重置。所以CyclicBarrier能处理更为复杂的业务场景。例如,如果计算发生错误,可以重置计数器,并让线程重新执行一次。
CyclicBarrier还提供其他有用的方法,比如
getNumberWaiting
方法可以获得CyclicBarrier
阻塞的线程数量。isBroken()
方法用来了解阻塞的线程是否被中断。